#!/usr/bin/python3
# -*- coding: utf-8 -*-

import os

import allure
import pytest
from playwright.sync_api import Page
from slugify import slugify

from databases.sqlite import SQLiteDB
from public.project_patch import DB
from public.report import SetupSession, TeardownSession, WeChatSend
from utils.log import log
from utils.rich_util import rich_table

global latest_report_link


def pytest_addoption(parser):
    parser.addoption(
        "--send-report",
        action="store_true",
        default=False,
        help="发送测试报告到webhook地址(默认: 不发送)",
    )
    parser.addoption(
        "--env",
        action="store",
        default="qingcloud",
        choices=["qingcloud", "testbm"],
        help="选择测试环境 (默认: qingcloud)",
    )


def setup_session():
    """
    全局前置
    :return:
    """
    SetupSession.setup_echo()
    SetupSession.load_yaml_config_to_cache()
    SetupSession.create_init_dir()
    SetupSession.delete_allure_json()
    SetupSession.setup_db_table()
    SetupSession.write_env_to_db()


def teardown_session():
    """
    全局后置
    :return:
    """
    global latest_report_link
    TeardownSession.mk_trend()
    TeardownSession.write_env_to_allure()
    TeardownSession.write_categories_to_allure()
    TeardownSession.generate_allure_report()
    latest_report_link = TeardownSession.send_report_to_allure_server().get('latest_report_link')


def pytest_configure(config):
    """对插件和 conftest 文件执行初始配置
    :param config: 访问配置值、插件管理器和插件挂钩。
    """
    env = config.getoption("--env")
    os.environ['TEST_ENV'] = env
    setup_session()


def pytest_unconfigure(config):
    """对插件和 conftest 文件执行清理
    :param config: 访问配置值、插件管理器和插件挂钩。
    """
    global latest_report_link
    teardown_session()
    send_report = config.option.send_report
    if send_report:
        if latest_report_link:
            WeChatSend(report_address=latest_report_link).send_msg()


def pytest_sessionstart(session):
    """在创建“Session”对象之后和执行收集之前调用并进入运行测试循环。
    :param pytest.Session session: pytest 会话对象.
    """
    pass


def pytest_sessionfinish(session, exitstatus):
    """在测试会话结束时调用。
    :param pytest.Session session: pytest 会话对象.
    :param int exitstatus: 退出状态码。
    """
    pass


# @pytest.mark.hookwrapper
# def pytest_runtest_makereport(item, call):
#     """作用：获取用例执行结果
#     :param item: 正执行的用例
#     :param call: CallInfo用例执行的结果
#     :return:
#     """
#     outcome = yield
#     report = outcome.get_result()
#     rich_table(
#         test_report=report,
#         test_phase=report.when,
#         test_id=report.nodeid,
#         run_results=report.outcome,
#         case_name=item.name,
#         case_path=item.fspath,
#         case_annotation=str(item.function.__doc__),
#         case_model=item.parent,
#         test_time=call.duration,
#         exception_info=call.excinfo,
#     )

def pytest_runtest_makereport(item, call) -> None:
    """
    截图到测试报告
    :param item:
    :param call:
    :return:
    """
    if call.when == "call":
        if call.excinfo is not None and "page" in item.funcargs:
            page: Page = item.funcargs["page"]
            allure.attach(
                page.screenshot(type='png'),
                name=f"{slugify(item.nodeid)}.png",
                attachment_type=allure.attachment_type.PNG
            )


def pytest_collection_modifyitems(session, config, items):
    """
    在收集完测试用例后，记录每个测试用例的详细信息到数据库
    :param session:
    :param config:
    :param items:
    :return:
    """
    sqlite = SQLiteDB(DB)
    for item in items:
        test_path = item.nodeid
        try:
            test_description = item.obj.__doc__.strip() if item.obj.__doc__ else ""
        except AttributeError:
            test_description = ""
        test_class_name = item.parent.name if item.parent else "root"
        test_function_name = item.name
        test_status = "collected"
        sqlite.insert_data(
            table="test_cases",
            data={
                "case_name": test_function_name,
                "class_name": test_class_name,
                "test_status": test_status,
                "description": test_description,
                "test_case_path": test_path,
            },
        )


def pytest_runtest_protocol(item, nextitem):
    """
    在每个测试用例运行前后更新其状态
    :param item:
    :param nextitem:
    :return:
    """
    log.info(f"正在执行测试用例: {item.nodeid}")
    sqlite = SQLiteDB(DB)
    if hasattr(item, "when"):
        test_status = "running" if item.when == "call" else "skipped"
    else:
        test_status = "unknown"
    sqlite.update_data(
        table="test_cases",
        data={"test_status": test_status},
        where={"test_case_path": item.nodeid},
    )


def pytest_terminal_summary(terminalreporter):
    """作用：统计所有用例执行结果
    :param terminalreporter: 内部终端报告器对象
    """
    passed_tests = len(terminalreporter.stats.get("passed", ""))
    failed_tests = len(terminalreporter.stats.get("failed", ""))
    skipped_tests = len(terminalreporter.stats.get("skipped", ""))
    error_tests = len(terminalreporter.stats.get("error", ""))
    xfailed_tests = len(terminalreporter.stats.get("xfailed", ""))
    xpassed_tests = len(terminalreporter.stats.get("xpassed", ""))
    total_tests = (
            passed_tests
            + failed_tests
            + skipped_tests
            + error_tests
            + xfailed_tests
            + xpassed_tests
    )

    terminalreporter.ensure_newline()
    terminalreporter.section("用例结果统计", sep="=")
    terminalreporter.line(f"用例的总数: {total_tests}")

    # 统计错误的断言信息
    terminalreporter.ensure_newline()
    terminalreporter.section("失败断言信息统计", sep="=")


def pytest_exception_interact(node, call, report):
    """
    异常处理
    :param node:
    :param call:
    :param report:
    :return:
    """
    log.warning("测试异常!")